package cn.rwklyd.BookKeeping.service.impl;

import cn.rwklyd.BookKeeping.mapper.mybatisPlus.CategoryMapper;
import cn.rwklyd.BookKeeping.mapper.mybatisPlus.TransactionMapper;
import cn.rwklyd.BookKeeping.pojo.Category;
import cn.rwklyd.BookKeeping.pojo.Transaction;
import cn.rwklyd.BookKeeping.pojo.vo.ChartDataVO;
import cn.rwklyd.BookKeeping.pojo.vo.RankingItemVO;
import cn.rwklyd.BookKeeping.service.StatisticsService;
import cn.rwklyd.BookKeeping.util.UserUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class StatisticsServiceImpl implements StatisticsService {

    @Autowired
    private TransactionMapper transactionMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public ChartDataVO getChartData(String type, String timeRange) {
        // 获取当前用户ID
        Long userId = UserUtils.getCurrentUserId();
        if (userId == null) {
            return new ChartDataVO();
        }

        // 获取时间范围
        Map<String, LocalDateTime> timeRangeMap = getTimeRange(timeRange);
        LocalDateTime startTime = timeRangeMap.get("start");
        LocalDateTime endTime = timeRangeMap.get("end");

        // 查询分类列表，用于判断收入/支出
        List<Category> categories = categoryMapper.selectList(
                new LambdaQueryWrapper<Category>()
                        .eq(Category::getUserId, userId)
                        .or()
                        .eq(Category::getUserId, 0) // 系统预设分类
                        .eq(Category::getIsDeleted, 0)
        );
        // 使用Integer作为key，解决类型不匹配问题
        Map<Integer, Category> categoryMap = categories.stream()
                .collect(Collectors.toMap(Category::getId, category -> category));

        // 查询交易记录
        List<Transaction> transactions = transactionMapper.selectList(
                new LambdaQueryWrapper<Transaction>()
                        .eq(Transaction::getUserId, userId)
                        .eq(Transaction::getIsDeleted, 0)
                        .between(Transaction::getTransactionDate, startTime, endTime)
        );

        // 过滤收入/支出
        boolean isExpense = "expense".equals(type);
        List<Transaction> filteredTransactions = transactions.stream()
                .filter(transaction -> {
                    // 将Long转为Integer来查找Category
                    Integer categoryId = transaction.getCategoryId().intValue();
                    Category category = categoryMap.get(categoryId);
                    return category != null && (isExpense ? category.getSpend() == 1 : category.getSpend() == 0);
                })
                .collect(Collectors.toList());

        // 构建图表数据
        ChartDataVO chartDataVO = buildChartData(filteredTransactions, timeRange, startTime);
        
        return chartDataVO;
    }

    @Override
    public List<RankingItemVO> getRankingList(String type, String timeRange) {
        // 获取当前用户ID
        Long userId = UserUtils.getCurrentUserId();
        if (userId == null) {
            return new ArrayList<>();
        }

        // 获取时间范围
        Map<String, LocalDateTime> timeRangeMap = getTimeRange(timeRange);
        LocalDateTime startTime = timeRangeMap.get("start");
        LocalDateTime endTime = timeRangeMap.get("end");

        // 查询分类列表，用于判断收入/支出
        List<Category> categories = categoryMapper.selectList(
                new LambdaQueryWrapper<Category>()
                        .eq(Category::getUserId, userId)
                        .or()
                        .eq(Category::getUserId, 0) // 系统预设分类
                        .eq(Category::getIsDeleted, 0)
        );
        // 使用Integer作为key，解决类型不匹配问题
        Map<Integer, Category> categoryMap = categories.stream()
                .collect(Collectors.toMap(Category::getId, category -> category));

        // 查询交易记录
        List<Transaction> transactions = transactionMapper.selectList(
                new LambdaQueryWrapper<Transaction>()
                        .eq(Transaction::getUserId, userId)
                        .eq(Transaction::getIsDeleted, 0)
                        .between(Transaction::getTransactionDate, startTime, endTime)
        );

        // 过滤收入/支出
        boolean isExpense = "expense".equals(type);
        List<Transaction> filteredTransactions = transactions.stream()
                .filter(transaction -> {
                    // 将Long转为Integer来查找Category
                    Integer categoryId = transaction.getCategoryId().intValue();
                    Category category = categoryMap.get(categoryId);
                    return category != null && (isExpense ? category.getSpend() == 1 : category.getSpend() == 0);
                })
                .collect(Collectors.toList());

        // 按分类统计金额
        Map<Integer, BigDecimal> categoryAmountMap = new HashMap<>();
        for (Transaction transaction : filteredTransactions) {
            Integer categoryId = transaction.getCategoryId().intValue();
            BigDecimal amount = transaction.getAmount();
            categoryAmountMap.put(categoryId, categoryAmountMap.getOrDefault(categoryId, BigDecimal.ZERO).add(amount));
        }

        // 计算总金额
        BigDecimal totalAmount = categoryAmountMap.values().stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 构建排行榜数据
        List<RankingItemVO> rankingList = new ArrayList<>();
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            for (Map.Entry<Integer, BigDecimal> entry : categoryAmountMap.entrySet()) {
                Integer categoryId = entry.getKey();
                BigDecimal amount = entry.getValue();
                Category category = categoryMap.get(categoryId);
                
                if (category != null) {
                    // 计算百分比
                    int percentage = amount.multiply(new BigDecimal("100"))
                            .divide(totalAmount, 0, RoundingMode.HALF_UP)
                            .intValue();
                    
                    RankingItemVO rankingItem = new RankingItemVO();
                    rankingItem.setName(category.getName());
                    rankingItem.setAmount(amount);
                    rankingItem.setPercentage(percentage);
                    rankingItem.setColor(category.getColor());
                    rankingItem.setIcon(category.getIcon());
                    
                    rankingList.add(rankingItem);
                }
            }
        }
        
        // 按金额降序排序
        rankingList.sort((a, b) -> b.getAmount().compareTo(a.getAmount()));
        
        return rankingList;
    }

    /**
     * 根据时间范围获取开始和结束时间
     */
    private Map<String, LocalDateTime> getTimeRange(String timeRange) {
        Map<String, LocalDateTime> result = new HashMap<>();
        LocalDate now = LocalDate.now();
        
        switch (timeRange) {
            case "week":
                // 本周一到本周日
                LocalDate monday = now.with(DayOfWeek.MONDAY);
                LocalDate sunday = now.with(DayOfWeek.SUNDAY);
                result.put("start", monday.atStartOfDay());
                result.put("end", sunday.atTime(LocalTime.MAX));
                break;
            case "month":
                // 本月第一天到本月最后一天
                LocalDate firstDayOfMonth = now.withDayOfMonth(1);
                LocalDate lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());
                result.put("start", firstDayOfMonth.atStartOfDay());
                result.put("end", lastDayOfMonth.atTime(LocalTime.MAX));
                break;
            case "year":
                // 本年第一天到本年最后一天
                LocalDate firstDayOfYear = now.withDayOfYear(1);
                LocalDate lastDayOfYear = now.with(TemporalAdjusters.lastDayOfYear());
                result.put("start", firstDayOfYear.atStartOfDay());
                result.put("end", lastDayOfYear.atTime(LocalTime.MAX));
                break;
            default:
                // 默认为周
                monday = now.with(DayOfWeek.MONDAY);
                sunday = now.with(DayOfWeek.SUNDAY);
                result.put("start", monday.atStartOfDay());
                result.put("end", sunday.atTime(LocalTime.MAX));
                break;
        }
        
        return result;
    }

    /**
     * 构建图表数据
     */
    private ChartDataVO buildChartData(List<Transaction> transactions, String timeRange, LocalDateTime startTime) {
        ChartDataVO chartDataVO = new ChartDataVO();
        
        // 计算总金额
        BigDecimal totalAmount = transactions.stream()
                .map(Transaction::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        chartDataVO.setTotalAmount(totalAmount);
        
        // 构建日期标签和数据
        List<String> labels = new ArrayList<>();
        Map<String, BigDecimal> dailyAmountMap = new HashMap<>();
        
        switch (timeRange) {
            case "week":
                // 周一到周日
                for (int i = 0; i < 7; i++) {
                    LocalDate date = startTime.toLocalDate().plusDays(i);
                    String label = getDayOfWeekLabel(date.getDayOfWeek());
                    labels.add(label);
                    dailyAmountMap.put(date.toString(), BigDecimal.ZERO);
                }
                
                // 计算每天的金额
                for (Transaction transaction : transactions) {
                    LocalDate date = transaction.getTransactionDate().toLocalDate();
                    String dateStr = date.toString();
                    if (dailyAmountMap.containsKey(dateStr)) {
                        BigDecimal amount = dailyAmountMap.get(dateStr);
                        dailyAmountMap.put(dateStr, amount.add(transaction.getAmount()));
                    }
                }
                
                // 计算平均值
                BigDecimal averageAmount = totalAmount.divide(new BigDecimal("7"), 2, RoundingMode.HALF_UP);
                chartDataVO.setAverageAmount(averageAmount);
                
                // 设置时间标签
                int weekOfYear = startTime.toLocalDate().get(WeekFields.ISO.weekOfYear());
                chartDataVO.setTimeLabel("第" + weekOfYear + "周");
                break;
                
            case "month":
                // 本月每一天
                int daysInMonth = startTime.toLocalDate().lengthOfMonth();
                for (int i = 0; i < daysInMonth; i++) {
                    LocalDate date = startTime.toLocalDate().plusDays(i);
                    String label = String.valueOf(date.getDayOfMonth());
                    labels.add(label);
                    dailyAmountMap.put(date.toString(), BigDecimal.ZERO);
                }
                
                // 计算每天的金额
                for (Transaction transaction : transactions) {
                    LocalDate date = transaction.getTransactionDate().toLocalDate();
                    String dateStr = date.toString();
                    if (dailyAmountMap.containsKey(dateStr)) {
                        BigDecimal amount = dailyAmountMap.get(dateStr);
                        dailyAmountMap.put(dateStr, amount.add(transaction.getAmount()));
                    }
                }
                
                // 计算平均值
                averageAmount = totalAmount.divide(new BigDecimal(daysInMonth), 2, RoundingMode.HALF_UP);
                chartDataVO.setAverageAmount(averageAmount);
                
                // 设置时间标签
                int currentMonth = startTime.getMonthValue();
                chartDataVO.setTimeLabel(currentMonth + "月");
                break;
                
            case "year":
                // 一年12个月
                for (int i = 1; i <= 12; i++) {
                    labels.add(i + "月");
                    LocalDate date = LocalDate.of(startTime.getYear(), i, 1);
                    dailyAmountMap.put(String.valueOf(i), BigDecimal.ZERO);
                }
                
                // 计算每月的金额
                for (Transaction transaction : transactions) {
                    int monthValue = transaction.getTransactionDate().getMonthValue();
                    String monthStr = String.valueOf(monthValue);
                    BigDecimal amount = dailyAmountMap.get(monthStr);
                    dailyAmountMap.put(monthStr, amount.add(transaction.getAmount()));
                }
                
                // 计算平均值
                averageAmount = totalAmount.divide(new BigDecimal("12"), 2, RoundingMode.HALF_UP);
                chartDataVO.setAverageAmount(averageAmount);
                
                // 设置时间标签
                int year = startTime.getYear();
                chartDataVO.setTimeLabel(year + "年");
                break;
        }
        
        // 构建图表数据
        List<BigDecimal> chartData = new ArrayList<>();
        for (int i = 0; i < labels.size(); i++) {
            String key;
            if (timeRange.equals("week")) {
                key = startTime.toLocalDate().plusDays(i).toString();
            } else if (timeRange.equals("month")) {
                key = startTime.toLocalDate().plusDays(i).toString();
            } else {
                key = String.valueOf(i + 1);
            }
            
            chartData.add(dailyAmountMap.getOrDefault(key, BigDecimal.ZERO));
        }
        
        chartDataVO.setChartData(chartData);
        chartDataVO.setLabels(labels);
        
        return chartDataVO;
    }

    /**
     * 获取星期几的标签
     */
    private String getDayOfWeekLabel(DayOfWeek dayOfWeek) {
        switch (dayOfWeek) {
            case MONDAY: return "周一";
            case TUESDAY: return "周二";
            case WEDNESDAY: return "周三";
            case THURSDAY: return "周四";
            case FRIDAY: return "周五";
            case SATURDAY: return "周六";
            case SUNDAY: return "周日";
            default: return "";
        }
    }
} 